/*-------------------------------------------------------------------------
 *
 * nodeSubqueryscan.c
 *	  扫描子查询（rangetable中的子选择）的支持例程。
 *
 * 这与sublinks（nodeSubplan.c）有足够的不同之处，因此我们需要两套代码。应该考虑尝试统一这些情况。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/executor/nodeSubqueryscan.c
 *
 *-------------------------------------------------------------------------
 */
/*
 * 接口例程
 *		ExecSubqueryScan			扫描一个子查询。
 *		ExecSubqueryNext			以顺序顺序检索下一个元组。
 *		ExecInitSubqueryScan		创建并初始化一个子查询扫描节点。
 *		ExecEndSubqueryScan			释放分配的任何存储。
 *		ExecReScanSubqueryScan		重新扫描关系
 *
 */
#include "postgres.h"

#include "executor/execdebug.h"
#include "executor/nodeSubqueryscan.h"

static TupleTableSlot *fc_SubqueryNext(SubqueryScanState *fc_node);

/* ----------------------------------------------------------------
 *						扫描支持
 * ----------------------------------------------------------------
 */
/* ----------------------------------------------------------------
 *		SubqueryNext
 *
 *		这是 ExecSubqueryScan 的主要部分
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_SubqueryNext(SubqueryScanState *fc_node)
{
	TupleTableSlot *fc_slot;

	/*
	 * 从子查询中获取下一个元组。
	 */
	fc_slot = ExecProcNode(fc_node->subplan);

	/*
	 * 我们只是返回子计划的结果槽，而不是额外消耗
	 * 周期来执行 ExecCopySlot()。（我们自己的 ScanTupleSlot 只用于
	 * EvalPlanQual 重新检查。）
	 */
	return fc_slot;
}

/*
 * SubqueryRecheck -- 访问方法例程以重新检查 EvalPlanQual 中的元组
 */
static bool fc_SubqueryRecheck(SubqueryScanState *fc_node, TupleTableSlot *fc_slot)
{
	
	return true;
}

/* ----------------------------------------------------------------
 *		ExecSubqueryScan(node)
 *
 *		顺序扫描子查询并返回下一个合格的
 *		元组。
 *		我们调用 ExecScan() 例程并将适当的
 *		访问方法函数传递给它。
 * ----------------------------------------------------------------
 */
static TupleTableSlot * fc_ExecSubqueryScan(PlanState *fc_pstate)
{
	SubqueryScanState *fc_node = castNode(SubqueryScanState, fc_pstate);

	return ExecScan(&fc_node->ss,
					(ExecScanAccessMtd) fc_SubqueryNext,
					(ExecScanRecheckMtd) fc_SubqueryRecheck);
}

/* ----------------------------------------------------------------
 *		ExecInitSubqueryScan
 * ----------------------------------------------------------------
 */
SubqueryScanState *
ExecInitSubqueryScan(SubqueryScan *fc_node, EState *fc_estate, int fc_eflags)
{
	SubqueryScanState *fc_subquerystate;

	/* 检查不支持的标志 */
	Assert(!(fc_eflags & EXEC_FLAG_MARK));

	/* SubqueryScan 不应该有任何“正常”的子节点 */
	Assert(outerPlan(fc_node) == NULL);
	Assert(innerPlan(fc_node) == NULL);

	/*
	 * 创建状态结构
	 */
	fc_subquerystate = makeNode(SubqueryScanState);
	fc_subquerystate->ss.ps.plan = (Plan *) fc_node;
	fc_subquerystate->ss.ps.state = fc_estate;
	fc_subquerystate->ss.ps.ExecProcNode = fc_ExecSubqueryScan;

	/*
	 * 杂项初始化
	 *
	 * 为节点创建表达式上下文
	 */
	ExecAssignExprContext(fc_estate, &fc_subquerystate->ss.ps);

	/*
	 * 初始化子查询
	 */
	fc_subquerystate->subplan = ExecInitNode(fc_node->subplan, fc_estate, fc_eflags);

	/*
	 * 初始化扫描槽和类型（ExecAssignScanProjectionInfo 需要）
	 */
	ExecInitScanTupleSlot(fc_estate, &fc_subquerystate->ss,
						  ExecGetResultType(fc_subquerystate->subplan),
						  ExecGetResultSlotOps(fc_subquerystate->subplan, NULL));

	/*
	 * 用作扫描元组的槽不是上面的槽（EPQ 外部），
	 * 而是来自下面节点的槽。
	 */
	fc_subquerystate->ss.ps.scanopsset = true;
	fc_subquerystate->ss.ps.scanops = ExecGetResultSlotOps(fc_subquerystate->subplan,
														&fc_subquerystate->ss.ps.scanopsfixed);
	fc_subquerystate->ss.ps.resultopsset = true;
	fc_subquerystate->ss.ps.resultops = fc_subquerystate->ss.ps.scanops;
	fc_subquerystate->ss.ps.resultopsfixed = fc_subquerystate->ss.ps.scanopsfixed;

	/*
	 * 初始化结果类型和投影。
	 */
	ExecInitResultTypeTL(&fc_subquerystate->ss.ps);
	ExecAssignScanProjectionInfo(&fc_subquerystate->ss);

	/*
	 * 初始化子表达式
	 */
	fc_subquerystate->ss.ps.qual =
		ExecInitQual(fc_node->scan.plan.qual, (PlanState *) fc_subquerystate);

	return fc_subquerystate;
}

/* ----------------------------------------------------------------
 *		ExecEndSubqueryScan
 *
 *		释放通过 C 例程分配的任何存储。
 * ----------------------------------------------------------------
 */
void ExecEndSubqueryScan(SubqueryScanState *fc_node)
{
	/*
	 * 释放表达式上下文
	 */
	ExecFreeExprContext(&fc_node->ss.ps);

	/*
	 * 清除上面的元组表
	 */
	if (fc_node->ss.ps.ps_ResultTupleSlot)
		ExecClearTuple(fc_node->ss.ps.ps_ResultTupleSlot);
	ExecClearTuple(fc_node->ss.ss_ScanTupleSlot);

	/*
	 * 关闭子查询
	 */
	ExecEndNode(fc_node->subplan);
}

/* ----------------------------------------------------------------
 *		ExecReScanSubqueryScan
 *
 *		重新扫描关系。
 * ----------------------------------------------------------------
 */
void ExecReScanSubqueryScan(SubqueryScanState *fc_node)
{
	ExecScanReScan(&fc_node->ss);

	/*
	 * ExecReScan 不知道我的子计划，所以我必须
	 * 自己进行参数更改信号。这也是好的，因为
	 * 子计划有自己控制其 chgParam 状态的内存上下文。
	 */
	if (fc_node->ss.ps.chgParam != NULL)
		UpdateChangedParamSet(fc_node->subplan, fc_node->ss.ps.chgParam);

	/*
	 * 如果子节点的chgParam不为空，则计划将由第一个ExecProcNode重新扫描。
	 */
	if (fc_node->subplan->chgParam == NULL)
		ExecReScan(fc_node->subplan);
}
